fix(content): automatically recalculate offset when header/footer changes - #30862
HappyKnuckles wants to merge 95 commits into
Conversation
|
@HappyKnuckles is attempting to deploy a commit to the Ionic Team on Vercel. A member of the Team first needs to authorize it. |
ShaneK
left a comment
There was a problem hiding this comment.
Thanks for your PR! This is looking really good! Aside from the broken tests, this is what I would like to see changed
|
@ShaneK any updates on this considering the review? |
ShaneK
left a comment
There was a problem hiding this comment.
I'm so sorry I didn't get back to this! I guess it was too close to Christmas and probably right before we took holiday break 🫠 I did notice some issues that I'm hoping won't be too hard to clean up, let me know if you need clarification
| if ('ResizeObserver' in window) { | ||
| let timeout: any; | ||
| this.resizeObserver = new ResizeObserver(() => { | ||
| clearTimeout(timeout); | ||
| timeout = setTimeout(() => this.resize(), 100); | ||
| }); |
There was a problem hiding this comment.
The timeout here lives inside the ResizeObserver callback closure, so disconnectObservers can't reach it to call clearTimeout. If a header or footer resizes within ~100ms of the content being removed from the DOM, the pending setTimeout still fires and calls this.resize(), which calls forceUpdate on a disconnected component.
Could you hoist this to a class field (something like private observerResizeTimeout: ReturnType<typeof setTimeout> | null = null) and clear it in disconnectObservers? The existing resizeTimeout field at the top of the class is doing the same thing for the onResize handler at line 192.
There was a problem hiding this comment.
I adjusted it in the latest commit.
However regarding the timeout: i noticed that the timeout is very noticeable visually.
Do you think it is worth switching to requestAnimationFrame?
There was a problem hiding this comment.
Awesome! Yeah, a rAF might be a better solution there, probably worth trying out. It should be a pretty quick test to see if it works better, basically the same structure, you'd even want to cache it and clear it out the same.
There was a problem hiding this comment.
rAF indeed seems to improve it, so i will go with that solution.
914cb59 to
95ddd56
Compare
…nges Previously, ion-content only calculated layout offsets on initialization or window resize. This caused content to be covered or obscured if a header or footer's dimensions changed dynamically (e.g. expanding a searchbar) or if they were added conditionally to the DOM. This implementation adds a ResizeObserver to watch sibling headers/footers for size changes and a MutationObserver to detect when they are added, triggering an automatic recalculation of the --offset-top and --offset-bottom CSS variables. Fixes ionic-team#26981
…afety for MutationObserver
091ced7 to
0a2fc35
Compare
|
If it helps anyone, we are currently patching this in manually and it's working well for us! Thank you for the fix. Looking forward to seeing it merged. |
|
@HappyKnuckles Just checking with you on this. We're still manually patching this thanks to your hard work -- is the PR ready for review or is there something I could help with to un-block the team? Looks like it was ready to me (save maybe rebasing on main at this point) but just curious. If it helps, I'd be happy to open a fresh PR with this rebased against main so you don't have to deal with the merge conflicts. Just let me know! |
…onic-team#31154) Issue number: resolves ionic-team#25470 --------- <!-- Please do not submit updates to dependencies unless it fixes an issue. --> <!-- Please try to limit your pull request to one type (bugfix, feature, etc). Submit multiple pull requests if needed. --> ## What is the current behavior? When an `<ion-tab-button>` declares an `href` with a query string or fragment, activating the tab drops both. In Angular, `IonTabs.select` navigates to `tabsPrefix/tab` and never reads the button's `href`. In Vue, the router splits the path on `?` and discards everything after it, and `IonTabBar` compares the raw href against the pathname so a tab with query params never shows as selected. In React, this issue has been fixed as part of the RR6 migration. ## What is the new behavior? Tab activation forwards the `href`'s query and fragment as navigation extras. A saved view's previously-captured extras still win when re-selecting a tab with prior history, so mid-stack state isn't clobbered. `IonTabBar`'s selection check now compares pathnames only, so a tab with a query string still highlights when its pathname is active. Added Playwright coverage in `@ionic/angular` and Cypress coverage in `@ionic/vue` for first visit, switching tabs, switching back, and re-clicking the active tab. The Vue tests will cause a conflict on merging into major-9.0, but I volunteer to fix that issue when it comes up. ## Does this introduce a breaking change? - [ ] Yes - [x] No <!-- If this introduces a breaking change: 1. Describe the impact and migration path for existing applications below. 2. Update the BREAKING.md file with the breaking change. 3. Add "BREAKING CHANGE: [...]" to the commit description when merging. See https://github.com/ionic-team/ionic-framework/blob/main/docs/CONTRIBUTING.md#footer for more information. --> ## Other information Preview pages: - Angular: https://ionic-framework-git-fw-7146-ionic1.vercel.app/angular/standalone/tabs-search-params/tab1?foo=bar - Vue: https://ionic-framework-git-fw-7146-ionic1.vercel.app/vue/tabs-search-params/tab1?foo=bar - React: This was fixed in the RR6 migration in V9. Unfortunately this won't be coming to 8.8 at this time.
…onic-team#31159) Issue number: resolves ionic-team#27843 --------- ## What is the current behavior? `createInlineOverlayComponent` renders inline overlays (modal, popover, etc.) inside a `<template>` at their declared JSX position. When the overlay presents, `CoreDelegate` teleports the DOM node into `ion-app`, but React's synthetic event delegation root stays at the original JSX parent. Once the overlay lives outside that subtree, React no longer dispatches events to children inside it, so `onClick`, `onChange`, and other handlers inside an `IonModal` rendered within an `IonNav` silently stop firing ## What is the new behavior? Top-level inline overlays now render through `createPortal` into the same `ion-app` container that `CoreDelegate` teleports into, so React's event root follows the DOM ## Does this introduce a breaking change? - [ ] Yes - [x] No ## Other information This issue also reports the same problem in Vue, but that was fixed in ionic-team#30227 Preview: [navigation-modal](https://ionic-framework-git-fw-6314-ionic1.vercel.app/react/navigation-modal) Dev build: ``` 8.8.8-dev.11779302602.17decfbf ```
Issue number: resolves internal --------- ## What is the current behavior? Playwright tests are hanging because of a [bug that breaks it for node 24.16+](microsoft/playwright#40724) ## What is the new behavior? Upgrading Playwright to 1.16.0 to fix the issue ## Does this introduce a breaking change? - [ ] Yes - [X] No ## Other information <!-- Any other information that is important to this PR such as screenshots of how the component looks before and after the change. -->
…-team#31178) Many of the controller tests are using the wrong path to import the overlay controllers. This results in those tests not working in Vercel. To reproduce the issue: 1. Open the [Action Sheet (main)](https://ionic-framework-git-main-ionic1.vercel.app/src/components/action-sheet/test/basic) preview 2. Attempt to open any Action Sheet 3. See the Action Sheet does not open and there is the following console error: `ReferenceError: actionSheetController is not defined` To see it working: 1. Open the [Action Sheet (branch)](https://ionic-framework-git-test-vercel-controllers-ionic1.vercel.app/src/components/action-sheet/test/basic) preview 2. Attempt to open any Action Sheet 3. See the Action Sheet opens without any errors This should be able to reproduce on any of the updated tests. Co-authored-by: Brandy Smith <6577830+brandyscarney@users.noreply.github.com>
Issue number: resolves ionic-team#19566, resolves ionic-team#19365 --------- <!-- Please do not submit updates to dependencies unless it fixes an issue. --> <!-- Please try to limit your pull request to one type (bugfix, feature, etc). Submit multiple pull requests if needed. --> ## What is the current behavior? Clicking an `ion-router-link` whose `href` contains a `#fragment` (e.g. `/catalog#pens`) fails to navigate and logs `not part of the routing set`. `parsePath` in `core/src/components/router/utils/path.ts` only splits on `?`, so the fragment gets folded into the last path segment and no registered route matches. Even when navigation succeeds, the matching anchor is never scrolled into view. ## What is the new behavior? `parsePath` extracts the fragment as a separate field, and `writeSegments` writes it back onto the URL via `pushState`. Routes match against the path segments alone, so `/catalog#pens` resolves to the `/catalog` route as expected. After `writeNavStateRoot` resolves, the router polls a few animation frames for an element matching the fragment inside the active page, then smooth-scrolls to it via `ion-content.scrollToPoint`, falling back to `Element.scrollIntoView` when the target sits outside `ion-content`. A `fragmentScrollToken` cancels any in-flight scroll the moment a newer `setSegments` runs. ## Does this introduce a breaking change? - [ ] Yes - [x] No <!-- If this introduces a breaking change: 1. Describe the impact and migration path for existing applications below. 2. Update the BREAKING.md file with the breaking change. 3. Add "BREAKING CHANGE: [...]" to the commit description when merging. See https://github.com/ionic-team/ionic-framework/blob/main/docs/CONTRIBUTING.md#footer for more information. --> ## Other information The fragment lookup scopes to the last `.ion-page:not(.ion-page-hidden)` in the document. Nested outlets can have multiple non-hidden pages mounted at once, and an identically-named anchor on a parent page would otherwise win over the leaf the user is viewing. Preview pages: - [router/basic](https://ionic-framework-git-fw-6958-ionic1.vercel.app/src/components/router/test/basic#/two)
Issue number: resolves stenciljs/core#6727 ## What is the current behavior? When a component property's default value is set to a JavaScript variable (e.g. `QUERY['lg']` on `ion-split-pane`'s `when` prop), Stencil emits the raw variable expression into the generated documentation files (`api.txt`, `components.d.ts`, and `dist/docs.json`). This causes the [component API docs](https://ionicframework.com/docs/api/split-pane#prop-when) to display `QUERY['lg']` instead of the resolved value `'(min-width: 992px)'`. ## What is the new behavior? Stencil 4.43.5 (via stenciljs/core#6728) resolves variable references at doc-generation time. After bumping and rebuilding: - `core/api.txt` — `ion-split-pane`'s `when` default is now `'(min-width: 992px)'` - `core/src/components.d.ts` — `@default` JSDoc tag reflects the resolved value - `dist/docs.json` (published as `@ionic/docs`) — will also carry the resolved value once released, enabling ionic-docs to remove the hardcoded fallbacks for the `gallery` component ## Does this introduce a breaking change? - [ ] Yes - [x] No ## Other information - Stencil changelog: https://github.com/stenciljs/core/releases/tag/v4.43.5 - Upstream fix: stenciljs/core#6728 - Once this is released as part of Ionic, a PR will be automatically created in `ionic-docs` using the updated `@ionic/docs` package — at that point the hardcoded default descriptions for `gallery`'s `columns` and `gap` props can be removed.
This PR contains the following updates: | Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) | |---|---|---|---| | [@capacitor/core](https://capacitorjs.com) ([source](https://redirect.github.com/ionic-team/capacitor)) | [`8.3.4` → `8.4.0`](https://renovatebot.com/diffs/npm/@capacitor%2fcore/8.3.4/8.4.0) |  |  | --- ### Release Notes <details> <summary>ionic-team/capacitor (@&ionic-team#8203;capacitor/core)</summary> ### [`v8.4.0`](https://redirect.github.com/ionic-team/capacitor/blob/HEAD/CHANGELOG.md#840-2026-06-02) [Compare Source](https://redirect.github.com/ionic-team/capacitor/compare/8.3.4...8.4.0) ##### Bug Fixes - **android:** show only the requested system bar ([#&ionic-team#8203;8480](https://redirect.github.com/ionic-team/capacitor/issues/8480)) ([4c6c321](https://redirect.github.com/ionic-team/capacitor/commit/4c6c3219afb5223211e857457e46283c37eb9424)) - **cli:** revert live reload config on failure ([#&ionic-team#8203;8485](https://redirect.github.com/ionic-team/capacitor/issues/8485)) ([1d031a4](https://redirect.github.com/ionic-team/capacitor/commit/1d031a4abec2c793079ba8897ad2e40c4cc6c7f9)) - **SystemBars:** make `safe-area-inset-x` available on API <= 34 ([#&ionic-team#8203;8424](https://redirect.github.com/ionic-team/capacitor/issues/8424)) ([e456de0](https://redirect.github.com/ionic-team/capacitor/commit/e456de083e19644f484bec5a5359cb67960ac8bc)) - **SystemBars:** respect `insetsHandling` disable ([#&ionic-team#8203;8481](https://redirect.github.com/ionic-team/capacitor/issues/8481)) ([d4ad7ff](https://redirect.github.com/ionic-team/capacitor/commit/d4ad7ffe39daf66e0cfc63af9028d5c05543bde7)) ##### Features - add method getDouble to plugin config ([#&ionic-team#8203;7638](https://redirect.github.com/ionic-team/capacitor/issues/7638)) ([93c72de](https://redirect.github.com/ionic-team/capacitor/commit/93c72de40a2ec4c78b33659250cb08340083088e)) - **cli:** add experimental packageOptions ([#&ionic-team#8203;8471](https://redirect.github.com/ionic-team/capacitor/issues/8471)) ([258867b](https://redirect.github.com/ionic-team/capacitor/commit/258867b7bf37b1837b99b02ec9638e5a6df08d97)) - **cli:** capture ios\_package\_manager in telemetry ([#&ionic-team#8203;8482](https://redirect.github.com/ionic-team/capacitor/issues/8482)) ([b4b297a](https://redirect.github.com/ionic-team/capacitor/commit/b4b297a52f8732659662d5e5aaeff81c0f7d9835)) #### [8.3.4](https://redirect.github.com/ionic-team/capacitor/compare/8.3.3...8.3.4) (2026-05-12) **Note:** Version bump only for package capacitor #### [8.3.3](https://redirect.github.com/ionic-team/capacitor/compare/8.3.2...8.3.3) (2026-05-08) ##### Bug Fixes - **cli:** copy plugin files in CocoaPods projects ([#&ionic-team#8203;8467](https://redirect.github.com/ionic-team/capacitor/issues/8467)) ([b2d7719](https://redirect.github.com/ionic-team/capacitor/commit/b2d771926a180e60deea31992d7d4abcd5ca3bc7)) #### [8.3.2](https://redirect.github.com/ionic-team/capacitor/compare/8.3.1...8.3.2) (2026-05-07) ##### Bug Fixes - **cli:** add cSettings support for compiler flags in generated Package.swift ([#&ionic-team#8203;8448](https://redirect.github.com/ionic-team/capacitor/issues/8448)) ([0bd0676](https://redirect.github.com/ionic-team/capacitor/commit/0bd0676315c5fd77e50312dd7b5bf4990dcbd7d0)) - **cli:** add system framework and weak framework support in SPM Package.swift ([#&ionic-team#8203;8447](https://redirect.github.com/ionic-team/capacitor/issues/8447)) ([3232f0f](https://redirect.github.com/ionic-team/capacitor/commit/3232f0fe1d9811b0b5c500e3dc05cb8a250177f8)) - **cli:** correct Capacitor plugin SPM compat check ([#&ionic-team#8203;8440](https://redirect.github.com/ionic-team/capacitor/issues/8440)) ([e5ccc45](https://redirect.github.com/ionic-team/capacitor/commit/e5ccc451dda27d56bca824ed644bd20fe4d988cb)) - **cli:** generate binaryTarget entries for custom xcframeworks in Package.swift ([#&ionic-team#8203;8445](https://redirect.github.com/ionic-team/capacitor/issues/8445)) ([1f7e33f](https://redirect.github.com/ionic-team/capacitor/commit/1f7e33fca43d183332ec19d22b0d75ef81d8cc6d)) - **cli:** generate resource entries in Package.swift ([#&ionic-team#8203;8455](https://redirect.github.com/ionic-team/capacitor/issues/8455)) ([790bd27](https://redirect.github.com/ionic-team/capacitor/commit/790bd27123497111984227010c3162cec94a108e)) - **cli:** handle Cordova plugins without iOS source files ([#&ionic-team#8203;8443](https://redirect.github.com/ionic-team/capacitor/issues/8443)) ([0da130e](https://redirect.github.com/ionic-team/capacitor/commit/0da130eb7a861bee4e2c35bc0aac53ba9c983fc3)) - **cli:** link plugin dependencies in Package.swift ([#&ionic-team#8203;8457](https://redirect.github.com/ionic-team/capacitor/issues/8457)) ([b3c769e](https://redirect.github.com/ionic-team/capacitor/commit/b3c769e856c826b1174518877cf86ac7ce73bf09)) - **ios:** support Cordova plugins with Package.swift ([#&ionic-team#8203;8438](https://redirect.github.com/ionic-team/capacitor/issues/8438)) ([139943b](https://redirect.github.com/ionic-team/capacitor/commit/139943b0c05fddb2d1ce2d6f468800fddf17b4cf)) - **SystemBars:** avoid extra view padding on API <= 34 ([#&ionic-team#8203;8439](https://redirect.github.com/ionic-team/capacitor/issues/8439)) ([5b135a7](https://redirect.github.com/ionic-team/capacitor/commit/5b135a70217be560e7176c8d5b514cc92ed3e4e4)) #### [8.3.1](https://redirect.github.com/ionic-team/capacitor/compare/8.3.0...8.3.1) (2026-04-16) ##### Bug Fixes - **android:** handle null versionName in isNewBinary() ([#&ionic-team#8203;8397](https://redirect.github.com/ionic-team/capacitor/issues/8397)) ([aa1a660](https://redirect.github.com/ionic-team/capacitor/commit/aa1a660f364f9b5f5a1e350e279c8864b04dd13b)) - boundary value extraction for form-data requests ([#&ionic-team#8203;7518](https://redirect.github.com/ionic-team/capacitor/issues/7518)) ([64ab854](https://redirect.github.com/ionic-team/capacitor/commit/64ab854c12330804c24275d88d3a9f7c8e52a73d)) - **cli:** check CAPACITOR\_COCOAPODS\_PATH in determinePackageManager ([#&ionic-team#8203;8407](https://redirect.github.com/ionic-team/capacitor/issues/8407)) ([acb64ab](https://redirect.github.com/ionic-team/capacitor/commit/acb64ab92a37ff53701cde453558e272e2e11eb6)) - **system-bars:** use separate current styles ([#&ionic-team#8203;8409](https://redirect.github.com/ionic-team/capacitor/issues/8409)) ([3d1f8d1](https://redirect.github.com/ionic-team/capacitor/commit/3d1f8d1b61480187375f5cd4de7ba999db007542)) </details> --- ### Configuration 📅 **Schedule**: (UTC) - Branch creation - "every weekday before 11am" - Automerge - At any time (no schedule defined) 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Never, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/ionic-team/ionic-framework). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yMDkuMCIsInVwZGF0ZWRJblZlciI6IjQzLjIwOS4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119--> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
…31190) This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [pozil/auto-assign-issue](https://redirect.github.com/pozil/auto-assign-issue) | action | major | `v3.0.0` → `v4.0.0` | --- ### Release Notes <details> <summary>pozil/auto-assign-issue (pozil/auto-assign-issue)</summary> ### [`v4.0.0`](https://redirect.github.com/pozil/auto-assign-issue/compare/v3.0.0...v4.0.0) [Compare Source](https://redirect.github.com/pozil/auto-assign-issue/compare/v3.0.0...v4.0.0) </details> --- ### Configuration 📅 **Schedule**: (UTC) - Branch creation - "every weekday before 11am" - Automerge - At any time (no schedule defined) 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Never, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/ionic-team/ionic-framework). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yMDkuMCIsInVwZGF0ZWRJblZlciI6IjQzLjIwOS4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119--> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
…31319) This PR contains the following updates: | Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) | |---|---|---|---| | [@capacitor/core](https://capacitorjs.com) ([source](https://redirect.github.com/ionic-team/capacitor)) | [`8.4.2` → `8.5.0`](https://renovatebot.com/diffs/npm/@capacitor%2fcore/8.4.2/8.5.0) |  |  | --- ### Release Notes <details> <summary>ionic-team/capacitor (@&ionic-team#8203;capacitor/core)</summary> ### [`v8.5.0`](https://redirect.github.com/ionic-team/capacitor/blob/HEAD/CHANGELOG.md#850-2026-07-31) [Compare Source](https://redirect.github.com/ionic-team/capacitor/compare/8.4.2...8.5.0) ##### Bug Fixes - **cli:** support TypeScript 7 when loading capacitor.config.ts ([#&ionic-team#8203;8534](https://redirect.github.com/ionic-team/capacitor/issues/8534)) ([4c1c870](https://redirect.github.com/ionic-team/capacitor/commit/4c1c8709413b9c19b008c99122ca330cc3c90e6f)) ##### Features - **cli:** add migrator functionality for adopting UIScene ([#&ionic-team#8203;8544](https://redirect.github.com/ionic-team/capacitor/issues/8544)) ([984fa85](https://redirect.github.com/ionic-team/capacitor/commit/984fa85ba0adab0aacf895aed6323bf4b503dccb)) - **ios:** UIScene Support ([#&ionic-team#8203;8536](https://redirect.github.com/ionic-team/capacitor/issues/8536)) ([3fa04a3](https://redirect.github.com/ionic-team/capacitor/commit/3fa04a357c92af34cd6fccb8124791963804a9dc)) #### [8.4.2](https://redirect.github.com/ionic-team/capacitor/compare/8.4.1...8.4.2) (2026-07-14) ##### Bug Fixes - **android:** explicitly grant URI permissions for image capture intent ([#&ionic-team#8203;8526](https://redirect.github.com/ionic-team/capacitor/issues/8526)) ([6f2d328](https://redirect.github.com/ionic-team/capacitor/commit/6f2d3283897a375d09ca9ec8784a9f0d65f0a530)) #### [8.4.1](https://redirect.github.com/ionic-team/capacitor/compare/8.4.0...8.4.1) (2026-06-19) ##### Bug Fixes - **cli:** make SPM dependency patch work on prereleases ([#&ionic-team#8203;8508](https://redirect.github.com/ionic-team/capacitor/issues/8508)) ([6048e90](https://redirect.github.com/ionic-team/capacitor/commit/6048e90171afa0229a3c25b52a23c377c6bb804c)) - **cli:** patch Capacitor SPM dependency version in plugins ([#&ionic-team#8203;8492](https://redirect.github.com/ionic-team/capacitor/issues/8492)) ([28bb2c6](https://redirect.github.com/ionic-team/capacitor/commit/28bb2c687069dfdd6aa7abc866004a1c6388d103)) </details> --- ### Configuration 📅 **Schedule**: (UTC) - Branch creation - "every weekday before 11am" - Automerge - At any time (no schedule defined) 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Never, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/ionic-team/ionic-framework). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC4zLjIiLCJ1cGRhdGVkSW5WZXIiOiI0NC4zLjIiLCJ0YXJnZXRCcmFuY2giOiJtYWluIiwibGFiZWxzIjpbXX0=--> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Issue number: resolves ionic-team#31315 --------- <!-- Please do not submit updates to dependencies unless it fixes an issue. --> <!-- Please try to limit your pull request to one type (bugfix, feature, etc). Submit multiple pull requests if needed. --> ## What is the current behavior? <!-- Please describe the current behavior that you are modifying. --> When an `ion-refresher`'s host page is unmounted while the native refresher is still being set up, Ionic throws an uncaught `TypeError` from the gesture utility: ``` TypeError: Cannot read properties of undefined (reading '__zone_symbol__addEventListener') at addEventListener (core/src/utils/gesture/listener.ts:21) at Object.enable (core/src/utils/gesture/index.ts) at Refresher.disabledChanged (core/src/components/refresher/refresher.tsx:125) at Refresher.setupMDNativeRefresher (core/src/components/refresher/refresher.tsx:372) ``` `setupMDNativeRefresher()` and `setupiOSNativeRefresher()` both do: ```ts this.gesture = (await import('../../utils/gesture')).createGesture({ el: this.scrollEl!, ... }); ``` The dynamic import yields to the event loop. If the refresher is disconnected while it resolves, `disconnectedCallback()` (line 547-549) has already run `this.scrollEl = undefined`, so `createGesture` receives `el: undefined`, and the subsequent `disabledChanged()` → `gesture.enable(true)` calls `addEventListener(undefined, ...)`, which throws. Because this happens inside an async method that nothing awaits, it surfaces as an **unhandled promise rejection** — it cannot be caught by a framework error boundary; this crashes my app in playwright sometimes when components are mounted very quickly. The `!` non-null assertions on `this.scrollEl` are what hide this from TypeScript. ## What is the new behavior? <!-- Please describe the behavior or changes that are being added by this PR. --> If the refresher is disconnected while the gesture module is being imported, setup aborts quietly. No gesture is created, and no error is thrown. ## Does this introduce a breaking change? - [ ] Yes - [ X] No <!-- If this introduces a breaking change: 1. Describe the impact and migration path for existing applications below. 2. Update the BREAKING.md file with the breaking change. 3. Add "BREAKING CHANGE: [...]" to the commit description when merging. See https://github.com/ionic-team/ionic-framework/blob/main/docs/CONTRIBUTING.md#footer for more information. --> ## Other information <!-- Any other information that is important to this PR such as screenshots of how the component looks before and after the change. --> --------- Co-authored-by: Shane <shane@shanessite.net>
…ken contributing link (ionic-team#31250) Issue number: resolves ionic-team#31251 --------- ## What is the current behavior? While working on e2e tests in the angular directory, I encountered a few documentation issues that made the local development workflow more difficult to follow. - The **"See Ionic's E2E testing guide"** link in `CONTRIBUTING.md` resolves to a 404 when viewed from GitHub's **Contributing** tab because the relative path is not resolved correctly. - Broken link: https://github.com/ionic-team/ionic-framework/blob/core/src/utils/test/playwright/docs/README.md - Intended destination: https://github.com/ionic-team/ionic-framework/blob/main/core/src/utils/test/playwright/docs/README.md - The Angular testing guide (`docs/angular/testing.md`) omits several steps that are necessary to successfully test local framework changes, including: - Removing existing `npm link` relationships before starting a new sync cycle. - Building `packages/angular-server`. - Installing Playwright browser dependencies before running Playwright tests for the first time. These omissions made it difficult to reproduce and validate additions to Angular tests I was adding as part of a separate PR. ## What is the new behavior? This PR does the following: - Fixes the broken link to the Ionic E2E testing guide in `CONTRIBUTING.md` by using a repository-root-relative path. - Updating `docs/angular/testing.md` to document the complete workflow for syncing local Angular framework changes. - Documents that `packages/angular-server` should be built as part of the local testing workflow. - Documents that contributors should remove previous `npm link` relationships before beginning a new sync cycle. - Clarifies the recommended workflow for syncing local package changes. - Documents the Playwright browser installation step required before running Playwright tests for the first time. ## Does this introduce a breaking change? - [ ] Yes - [x] No ## Other information --------- Co-authored-by: Maria Hutt <thetaPC@users.noreply.github.com>
…am#31153) Issue number: resolves ionic-team#31152 ## What is the current behavior? In `IonRouter.handleHistoryChange`, the URL-change guard compares mismatched operands: ```ts const leavingUrl = leavingLocationInfo.pathname + leavingLocationInfo.search; if (leavingUrl !== location.pathname) { ... } ``` The left side includes `search`, the right side does not. For any route with a non-empty query string, the comparison is **always** unequal, so the transition block runs on every history event — including no-op popstates over same-URL entries pushed via `window.history.pushState`. Concretely: when a same-URL history entry on a search-bearing route is popped, `action === 'POP'` is processed and the IRO transitions to `currentRoute.pushedByRoute`. The browser URL doesn't change but the rendered view does — the user gets silently teleported to a different page in the stack. Minimal repro: on any search-bearing route, run in the console: ```js window.history.pushState({}, '', window.location.href); window.history.back(); ``` The current behavior swaps the rendered page to the previous entry in `locationHistory` while the URL stays put. Expected: no visible change. Full repro and analysis in ionic-team#31152. This is also the root cause behind the symptom reported in ionic-team#25534 (framed there as a transition-rerender flash). ## What is the new behavior? The right side of the comparison now also includes `search`, so the block only runs when the URL actually changed: ```ts const leavingUrl = leavingLocationInfo.pathname + leavingLocationInfo.search; const currentUrl = location.pathname + (location.search || ''); if (leavingUrl !== currentUrl) { ... } ``` Behavior matrix: - Search-only navigations (e.g. `routerPush(samePath + newSearch)`): **unchanged** — pathname matches but search differs, block still runs. - Pathname changes: **unchanged** — pathnames differ, block still runs. - No-op popstates on search-bearing routes: **fixed** — pathname + search both match, block correctly skipped. A new Cypress regression test is included (`packages/react-router/test/base/tests/e2e/specs/routing.cy.js`) that pushes a same-URL state on a search-bearing route, calls `history.back()`, and asserts the page does not teleport. ## Does this introduce a breaking change? - [ ] Yes - [x] No The comparison becomes stricter (skips the block in more cases than before), but only for the cases where the URL didn't actually change. All cases where the URL *did* change are still routed through the existing transition logic unchanged. ## Other information Happy to iterate on the fix shape if there's a preferred alternative — e.g. gating behind a flag for backward compat, or restructuring the guard differently. The one-line change above is the smallest possible fix that keeps existing behavior for every "real URL change" case. --------- Co-authored-by: ShaneK <shane@shanessite.net>
…ty (ionic-team#31249) ## Summary Fixes ionic-team#18550 — `ion-fab-button` with `type="submit"` does not submit its parent form. **Root cause:** `ion-fab-button` uses Shadow DOM, so its internal `<button>` element is not part of the form-associated elements in the light DOM. Clicking it (or pressing Enter in a text field) never triggers the form's `submit` event. **Fix:** Port the hidden-button pattern already used by `ion-button`: - On render (when `type !== 'button'`), inject a hidden `<button>` into the associated form in the light DOM. - On click, call `ev.preventDefault()` to stop the shadow button from acting, then `.click()` the hidden button — which is a proper form participant and triggers submission. - Add a `form` prop (`string | HTMLFormElement`) so the button can target a form outside its DOM subtree (same API as `ion-button`). - Keep the `disabled` watcher in sync with the hidden button so a disabled fab-button cannot submit. ## Changes - `core/src/components/fab-button/fab-button.tsx` — core fix - `core/src/components/fab-button/test/form/fab-button.e2e.ts` — Playwright e2e tests (closest form, form by id, form by reference, Enter key, disabled, reset) - `core/src/components/fab-button/test/form/fab-button.spec.ts` — unit test asserting the hidden button is not duplicated on re-renders - `core/src/components/fab-button/test/form/index.html` — visual test page ## Test plan - [ ] `npm run test.spec` passes (unit test for hidden-button dedup) - [ ] `npm run test.e2e` passes for `fab-button/test/form/fab-button.e2e.ts` - [ ] Manual: `ion-fab-button type="submit"` inside a `<form>` submits on click - [ ] Manual: pressing Enter in a form input triggers submit - [ ] Manual: `ion-fab-button type="reset"` resets form fields - [ ] Manual: disabled fab-button does not submit - [ ] Manual: `form="id"` targets an external form --------- Co-authored-by: ShaneK <shane@shanessite.net>
Issue number: internal
---------
## What is the current behavior?
Currently, `prepareLazyLoaded` in `ion-tab` sets `loaded = true` before
it attempts the attach, and the `try/catch` wrapped around
`attachComponent` is synchronous while `attachComponent` itself is
`async`. So when the first attach rejects, the rejection skips the
`catch` entirely and `loaded` is already `true`, which means the `if
(!this.loaded && ...)` guard blocks every later attempt. The tab renders
as an empty page for the rest of the session, even if whatever caused
the failure is fixed.
`ion-tabs` activates a tab by setting `active` rather than by awaiting
the tab's `setActive()`, so the failure goes through the
`@Watch('active')` path. That call isn't awaited, so on rejection the
error surfaces as an unhandled promise rejection and `printIonError`
never runs. `select()` resolves successfully while the tab is blank, so
there's no signal to the caller either.
## What is the new behavior?
With this change, `prepareLazyLoaded` caches the in-flight attach
promise instead of flipping a boolean up front. Concurrent activations
still share a single attempt, which is what the old `loaded = true`
ordering was providing, but the cache is only cleared when the attach
rejects, so the next activation retries. The unawaited call in the
`active` watcher now has a `.catch` that routes the error to
`printIonError`, which is what the unreachable `catch` block was there
for.
`setActive()` still rejects on a failed attach and still leaves `active`
as `false`, so that part is unchanged.
## Does this introduce a breaking change?
- [ ] Yes
- [X] No
## Other information
There's no stock test page that reproduces the failure, since it needs a
delegate that rejects, but the tabs pages confirm normal lazy loading
still works:
- [Tabs:
Basic](https://ionic-framework-git-fw-7296-ionic1.vercel.app/src/components/tabs/test/basic)
…-team#31335) Issue number: resolves ionic-team#31155, resolves ionic-team#31143 --------- ## What is the current behavior? Currently, an `ion-datetime` inside a modal or popover shows the wrong month once the overlay is reopened. The selected day isn't visible, the previous month button does nothing, and picking a day from the grid lands on an unrelated date. Overlays move their host element into `ion-app` when presenting and back to its original position when dismissing, which disconnects and reconnects the datetime. `disconnectedCallback` reset `hasBeenIntersecting` during that move, so by the time the hidden-state `IntersectionObserver` entry arrived, `hiddenCallback` mistook the dismissal for the synthetic initial entry and returned early. That left `datetime-ready` on the host, so on the next present `markReady` saw the class and returned without re-centering the calendar on the working month, and the browser had already reset `scrollLeft` to 0 while the overlay was hidden. `scrollLeft: 0` renders the previous month's grid while the header still names the working month, which is what produces all three symptoms. ## What is the new behavior? With this change, `disconnectedCallback` no longer resets `hasBeenIntersecting`. That flag tracks the observers, and the observers are only created in `componentDidLoad` and never re-created on reconnect, so a DOM move has no business clearing it. `hiddenCallback` now sees the real hidden transition on dismiss and tears down as it did before ionic-team#31108, which lets `markReady` run again on the next present and re-center the calendar. ## Does this introduce a breaking change? - [ ] Yes - [X] No ## Other information <!-- Any other information that is important to this PR such as screenshots of how the component looks before and after the change. --> This regression was introduced in ionic-team#31108. That PR needed the flag to make `hiddenCallback` ignore the synthetic initial entry, but the `disconnectedCallback` reset it also added had no job and broke the overlay case. Before ionic-team#31108, `hiddenCallback` had no guard at all and always removed `datetime-ready` on dismiss, so this restores the behavior that shipped for all of v8.. - [Relevant test screen - iOS](https://ionic-framework-git-fix-31155-ionic1.vercel.app/src/components/datetime-button/test/overlays?ionic:mode=ios) - [Relevant test screen - MD](https://ionic-framework-git-fix-31155-ionic1.vercel.app/src/components/datetime-button/test/overlays?ionic:mode=md) To reproduce: open the "Modal - Default" picker, dismiss it, then open it again. On `main` the grid shows February while the header reads March 2022. --------- Co-authored-by: Maria Hutt <thetaPC@users.noreply.github.com>
…m#31323) This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [github/codeql-action](https://redirect.github.com/github/codeql-action) | action | patch | `v4.37.4` → `v4.37.5` | --- ### Release Notes <details> <summary>github/codeql-action (github/codeql-action)</summary> ### [`v4.37.5`](https://redirect.github.com/github/codeql-action/compare/v4.37.4...v4.37.5) [Compare Source](https://redirect.github.com/github/codeql-action/compare/v4.37.4...v4.37.5) </details> --- ### Configuration 📅 **Schedule**: (UTC) - Branch creation - "every weekday before 11am" - Automerge - At any time (no schedule defined) 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Never, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/ionic-team/ionic-framework). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC4zLjIiLCJ1cGRhdGVkSW5WZXIiOiI0NC4zLjIiLCJ0YXJnZXRCcmFuY2giOiJtYWluIiwibGFiZWxzIjpbXX0=--> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
…m#31340) This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [github/codeql-action](https://redirect.github.com/github/codeql-action) | action | patch | `v4.37.5` → `v4.37.6` | --- ### Release Notes <details> <summary>github/codeql-action (github/codeql-action)</summary> ### [`v4.37.6`](https://redirect.github.com/github/codeql-action/releases/tag/v4.37.6) [Compare Source](https://redirect.github.com/github/codeql-action/compare/v4.37.5...v4.37.6) - Changed the default filepath for the new remote file address format that was introduced in CodeQL Action 4.37.0 / 3.37.0 to `.github/codeql-config.yml` to align it with the suggested path that is used elsewhere. [#&ionic-team#8203;4070](https://redirect.github.com/github/codeql-action/pull/4070) </details> --- ### Configuration 📅 **Schedule**: (UTC) - Branch creation - "every weekday before 11am" - Automerge - At any time (no schedule defined) 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Never, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/ionic-team/ionic-framework). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC4xMi4wIiwidXBkYXRlZEluVmVyIjoiNDQuMTIuMCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==--> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Issue number: resolves ionic-team#31333 --------- ## What is the current behavior? Currently, Stencil's lazy loader resolves component bundles with a template literal dynamic import wrapped in a `/* @vite-ignore */` comment. Vite 8 moved dependency prebundling from esbuild to Rolldown, which honors that comment and leaves the import alone, so the `.entry.js` chunks never get emitted into the prebundle output. Requests for them 404 and no lazy component registers, which surfaces as `Constructor for "ion-app#undefined" was not found` in the console. Angular CLI 22.1 is where most people hit this, since its dev server is the first to ship Vite 8. It only affects the lazy `IonicModule` path, because that's the only thing in the repo that pulls `@ionic/core/loader`. Standalone components import from `@ionic/core/components` and never touch the loader. ## What is the new behavior? With this change we set `extras.enableImportInjection` in `core/stencil.config.ts`. Stencil prepends a switch of literal import paths to the loader, ahead of the existing `@vite-ignore` import, so Rolldown can resolve each bundle and emit its chunk. The original import stays as the fallback, so nothing changes for consumers that already worked. The fix is one config flag that would silently revert if anyone dropped it, so there's a guard alongside it. `core/scripts/verify/lazy-imports.js` reads the built `dist/esm` and `dist/cjs`, locates the loader chunk, and fails if any bundle is missing a literal import. It runs in both `build-core` and `build-core-stencil-prerelease`, so a Stencil upgrade that changes the injection behavior fails the nightly rather than shipping. ## Does this introduce a breaking change? - [ ] Yes - [X] No ## Other information Verified against a stock Angular CLI 22.1 app using `IonicModule.forRoot()` and a local build of this branch. Before the fix we had a ton of 404s on `ion-app_8.entry.js` and `ion-button_2.entry.js`, plus some `Constructor for ... was not found` errors. After the fix we had no console errors, components render fine, and `ng build` emits the entry chunks with the right names. CI doesn't cover the actual symptom and it cannot here because we have no ng22 test apps on main, but we'll have it tested for free on major-9.0 with the existing ng22 test app there. The guard script asserts the build-output invariant for the extra validation. The injected switch adds roughly 10.8 KB raw to the lazy runtime chunk, which is under 1 KB gzipped because the case clauses are near-identical repeated text, but people using the CDN build will have no extra overhead at all. Anyone on a published `@ionic/core` can work around this with `prebundle.exclude` in `angular.json` until it ships: ```json "serve": { "options": { "prebundle": { "exclude": ["@ionic/angular", "@ionic/core"] } } } ```
…am#31339) Issue number: resolves ionic-team#31332 --------- ## What is the current behavior? Currently, pulling down inside a sheet or card modal dismisses the modal instead of triggering the refresher when the content uses a custom scroll host. ## What is the new behavior? With this change, the gesture resolves the refresher from the enclosing `ion-content` when the swipe target is a custom scroll host, so pulling down runs the refresher and leaves the modal alone. The new `findRefresherInContent` util only reports a refresher for the scroll host that refresher actually scrolls with, which is the first one in the content, so a second unrelated scroll host still swipes the modal normally. The `expandToScroll: false` path now runs the same check. ## Does this introduce a breaking change? - [ ] Yes - [X] No ## Other information Test coverage is 4 e2e tests on the new page: [Test page (iOS)](https://ionic-framework-git-fix-31332-ionic1.vercel.app/src/components/modal/test/refresher-scroll-target/?ionic:mode=ios) [Test page (MD)](https://ionic-framework-git-fix-31332-ionic1.vercel.app/src/components/modal/test/refresher-scroll-target/?ionic:mode=md) ## Current dev build: ``` 8.8.18-dev.11786385944.17aca7e5 ```
…1313) Issue number: resolves ionic-team#31312 --------- <!-- Please do not submit updates to dependencies unless it fixes an issue. --> <!-- Please try to limit your pull request to one type (bugfix, feature, etc). Submit multiple pull requests if needed. --> ## What is the current behavior? Current docs don't provide recommended testing guidance for accurately testing standalone components. This can create issues because a standalone (custom-elements) build doesn't expose `el.componentOnReady()`, so any test that reads fixture.nativeElement before hydration in a standalone project has the potential to not be testing what they think they are. ## What is the new behavior? - Adds a couple of sentences to talk about using the existing `componentOnReady` helper from `@ionic/core` in Angular's `testing.md` file - Adds the same standalone guidance to the `testing.md` files for React and Vue ## Does this introduce a breaking change? - [ ] Yes - [x] No --------- Co-authored-by: Maria Hutt <thetaPC@users.noreply.github.com>
…tep (ionic-team#31310) Issue number: N/A --------- <!-- Please do not submit updates to dependencies unless it fixes an issue. --> <!-- Please try to limit your pull request to one type (bugfix, feature, etc). Submit multiple pull requests if needed. --> ## What is the current behavior? <!-- Please describe the current behavior that you are modifying. --> The Playwright npm packages and the `mcr.microsoft.com/playwright` Docker image in `core/Dockerfile` are tracked by two separate Renovate managers. The Docker image is published to the container registry about a day after the npm release, so the two updates become available at different times. Because they are on different datasources, the npm bump opens a PR before the Docker tag is ready, and getting the Dockerfile into the same PR requires a manual rebase once the image lands. This is easy to miss, and merging the npm bump alone leaves `core/package.json` and `core/Dockerfile` on mismatched Playwright versions. ## What is the new behavior? <!-- Please describe the behavior or changes that are being added by this PR. --> - A custom manager now resolves the `core/Dockerfile` Playwright image tag from the npm `@playwright/test` release instead of the container registry, so both files bump from the same version. - The built-in Docker manager for that image is disabled so it no longer produces a separate, lagging update. - The Dockerfile is added to the grouped `playwright` rule, and a `minimumReleaseAge` of 3 days lets the matching image publish before the PR opens. - Net result is a single `playwright` PR that updates `core/package.json` and `core/Dockerfile` together, with no manual rebase. ## Does this introduce a breaking change? - [ ] Yes - [ ] No <!-- If this introduces a breaking change: 1. Describe the impact and migration path for existing applications below. 2. Update the BREAKING.md file with the breaking change. 3. Add "BREAKING CHANGE: [...]" to the commit description when merging. See https://github.com/ionic-team/ionic-framework/blob/main/docs/CONTRIBUTING.md#footer for more information. --> ## Other information <!-- Any other information that is important to this PR such as screenshots of how the component looks before and after the change. --> This is a Renovate config change only, so it will not take effect until the next scheduled Renovate run. If the Docker image for a given release has not been published yet when CI runs, the Dockerfile build can fail on a missing tag; the fix is to wait for the image to appear and re-run the job, with no code change needed. This is documented inline in `renovate.json5`.
…am#31343) Issue number: resolves ionic-team#31337 --------- Currently, `ion-content` caches its fullscreen offsets and only recomputes them on load, a `window` resize, `ionTabBarLoaded`, or `recalculateDimensions()`. A change to `--ion-safe-area-top` fires none of those, so on Android edge-to-edge, where the real inset only arrives once the native WindowInsets callback has run, the fullscreen scroll region and `#background-content` stay short by the inset and leave a gap behind a translucent header. Sheet modals have the same problem: `--ion-modal-offset-top` is a resolved pixel value written at present time, so a sheet presented before the inset is known stays too tall. Fullscreen `ion-content` now observes its own box with a `ResizeObserver`, which works because the content flexes to fill whatever space the headers and footers leave behind. The callback keeps the `offsetParent === null` guard from `onResize()`, since a hidden page reports a 0x0 box and recomputing then would zero the offsets, but it skips that method's 100ms debounce, which left the offsets visibly wrong for a small amount of time. Sheet modals subscribe to `onRootSafeAreaTopChange()`, a hidden probe element whose `height` tracks `var(--ion-safe-area-top)` so a change to the variable becomes an observable size change. - [ ] Yes - [X] No The preview pages won't show the bug on their own, since it needs the inset to change after load. Run `document.documentElement.style.setProperty('--ion-safe-area-top', '24px')` in the console to trigger it. - [Fullscreen content (iOS)](https://ionic-framework-git-fix-31337-ionic1.vercel.app/src/components/content/test/fullscreen?ionic:mode=ios) / [Fullscreen content (MD)](https://ionic-framework-git-fix-31337-ionic1.vercel.app/src/components/content/test/fullscreen?ionic:mode=md) - [Modal safe-area (iOS)](https://ionic-framework-git-fix-31337-ionic1.vercel.app/src/components/modal/test/safe-area?ionic:mode=ios) / [Modal safe-area (MD)](https://ionic-framework-git-fix-31337-ionic1.vercel.app/src/components/modal/test/safe-area?ionic:mode=md)
…m#31357) This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [github/codeql-action](https://redirect.github.com/github/codeql-action) | action | patch | `v4.37.6` → `v4.37.7` | --- ### Release Notes <details> <summary>github/codeql-action (github/codeql-action)</summary> ### [`v4.37.7`](https://redirect.github.com/github/codeql-action/releases/tag/v4.37.7) [Compare Source](https://redirect.github.com/github/codeql-action/compare/v4.37.6...v4.37.7) - Update default CodeQL bundle version to [2.26.3](https://redirect.github.com/github/codeql-action/releases/tag/codeql-bundle-v2.26.3). [#&ionic-team#8203;4085](https://redirect.github.com/github/codeql-action/pull/4085) </details> --- ### Configuration 📅 **Schedule**: (UTC) - Branch creation - "every weekday before 11am" - Automerge - At any time (no schedule defined) 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Never, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/ionic-team/ionic-framework). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC4yNC4wIiwidXBkYXRlZEluVmVyIjoiNDQuMjQuMCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==--> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
…ic-team#31246) Issue number: resolves ionic-team#30226 --------- ## What is the current behavior? When `dir="rtl"` is set on `<ion-app>` (but not on `document`), the hamburger menu appears on the right side visually, but the **open animation still slides in from the left**. This happens because `isEndSide()` only checked `document.dir`, not the nearest ancestor `dir` attribute. ## What is the new behavior? - `isEndSide()` takes an optional host element and delegates to `isRTL()` instead of reading `document.dir` itself. - `isRTL()` now walks up from the given element to the nearest ancestor that declares a `dir`, so `<ion-app dir="rtl">` applies to everything inside it even when the document is `ltr`. It previously read only the element's own `dir` before falling back to `document.dir`. - `sideChanged()` in `ion-menu` also passes the menu element so the side is correct before the first animation. - Unit tests added for the `ion-app dir="rtl"` case. ## Does this introduce a breaking change? - [ ] Yes - [x] No --------- Co-authored-by: Brandy Smith <6577830+brandyscarney@users.noreply.github.com> Co-authored-by: Austin-Spriggs <aspriggs@madisoncollege.edu>
…eck (ionic-team#31363) Issue number: resolves ionic-team#30539 --------- ## What is the current behavior? Currently, `supportsPassive` in the gesture listener utils detects passive listener support by attaching a real `optsTest` listener to the element and reading the `passive` getter, but it never removes that listener. The result is a listener permanently attached to whichever element runs the first gesture. There's a second failure mode in the same function. The `_sPassive === undefined` guard only clears if the getter fires or if `addEventListener` throws, so a runtime that does neither leaves `_sPassive` undefined and every subsequent call attaches another `optsTest` listener that's never cleaned up. ## What is the new behavior? We now always pass the options object, and the detection is removed entirely. The check was already dead code, because passive options object support landed in Chrome 51, Safari 10, Firefox 49, and Edge 16, all in 2016, while our lowest supported versions are Chrome 89, Safari 15, Firefox 75, and Edge 89. The function returned `true` on every browser we support, so both branches of the removed ternary collapse to the same value and there's no behavior change. ## Does this introduce a breaking change? - [ ] Yes - [X] No ## Other information
…31365) Issue number: resolves ionic-team#31026 --------- Opening an `ion-range` with `dualKnobs` set to `true` with a value set inside of a modal does not render the values properly. - Added a watcher for `dualKnobs` that calls `updateRatio()`, ensuring knob ratios are recomputed whenever `dualKnobs` changes. The `min`, `max`, and `value` properties already had watchers. `dualKnobs` was the only ratio-affecting property without one. - Knobs are now positioned correctly regardless of whether `value` or `dualKnobs` is assigned first. They are also repositioned correctly when `dualKnobs` is toggled at runtime in either direction. - Range now sets the correct values when used inside a modal, including when `dualKnobs` and `value` are assigned in different orders. - Improved the `dualKnobs` property description. - Added e2e tests covering both property assignment orders and runtime toggling of `dualKnobs`. - [ ] Yes - [x] No --------- Co-authored-by: Brandy Smith <6577830+brandyscarney@users.noreply.github.com> Co-authored-by: pratyushjaiswal0806-dot <pratyushjaiswal0806@gmail.com>
de18daa to
ed06d3c
Compare
Sorry for leaving you in the dust. I had problems with the tests as CI was failing and locally it wasn't. However i believe this PR is not needed anymore as i think the problem was fixed as a side effect in this #31343. |
|
@HappyKnuckles Interesting. That does look like it should fix it! I'll test that out. Is that patch working for you if you've tried it? |
I ran my test suite and looked at the preview. Test suite passed and it looked like it is working correctly in the manual check. |
Issue number: resolves #26981
What is the current behavior?
Currently, ion-content calculates its layout offsets (--offset-top/bottom) only on initialization or window resize.
If a developer dynamically changes the height of a header or footer (e.g., expanding a searchbar, wrapping text) or adds a toolbar conditionally to the DOM (e.g., via *ngIf), the ion-content does not adjust. This results in content being obscured behind the header/footer.
What is the new behavior?
This PR adds a ResizeObserver and MutationObserver to ion-content to automatically handle dynamic layout changes.
ResizeObserver watches sibling and elements and triggers a recalculation of --offset-top and --offset-bottom whenever their dimensions change.
MutationObserver watches the parent element to detect when or elements are added to or removed from the DOM.
Removes the need for developers to manually listen for standard dynamic layout changes.
Does this introduce a breaking change?
Other information
I have added a new E2E test suite in core/src/components/content/test/auto-offset which covers:
Visual Regression: Ensures no regressions on initial load.
Functional Header: Expands a header dynamically and asserts that content --offset-top update correctly.
Functional Footer: Expands a footer dynamically and asserts that content --offset-bottom update correctly.
Visual Confirmation: Snapshots verify that the content is physically pushed down/up when toolbars expand.
Before:

After:
